package com.example.springbootdemo.text.zh;


import java.io.IOException;
import java.util.*;

/**
 * 文本相似度计算
 * @author
 * @create
 */
public class SimCale {

    private WordsHelper wordsHelper = new WordsHelper();

    // 相似度阈值
    public static final Double SIM_THRESHOLD = 0.5d;

    /**
     * 计算相似度
     * @param ywmap 文章key, 文章内容
     * @return map  相似的文章key的set集合
     */
    public List<Set<String>> sim(Map<String, String> ywmap) {
        // 分词(文章，分词)
        HashMap<String, String> doc_words = new HashMap<>();
        // 词库
        List<String> allwords = new ArrayList<>();
        ywmap.entrySet().forEach(entry -> {
            Map<String, Integer> wordMap = wordsHelper.parseWordCounts(entry.getValue());
            StringBuilder sb = new StringBuilder();
            wordMap.entrySet().forEach(e -> {
                // 每个单词空格分割,添加到文章词库
                sb.append(e.getKey()).append(" ");
                // 添加到总词库
                allwords.add(e.getKey());
            });
            doc_words.put(entry.getKey(), sb.toString());
        });

        //3.计算每个文档的单词的tfidf值
        HashMap<String, Map<String, Float>> docs_tfidf = new HashMap<>();
        for(Map.Entry<String, String> entry:doc_words.entrySet()){
            String words = entry.getValue();
            //获取文档的tf
            Map<String, Float> tf = TFIDFCale.tfCalculate(words);
            //获取文档的tfidf
            Map<String, Float> tfidfCalculate = null;
            try {
                tfidfCalculate = TFIDFCale.tfidfCalculate(doc_words.entrySet().size(), doc_words, tf);
                docs_tfidf.put(entry.getKey(), tfidfCalculate);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 根据词集，词频（tfidf）,构建向量
        // 每一篇文章创建向量(文章,向量)
        HashMap<String, double[]> xlmap = new HashMap<>();
        doc_words.entrySet().stream().forEach(e -> {
            // 文章，向量
            xlmap.put(e.getKey(), new double[allwords.size()]);
        });
        XlCount xlCount = new XlCount();
        for (int i = 0; i < allwords.size(); i++) {
            xlCount.setXlCount(i);
            // 构建所有文章向量(文章，tfidf)
            docs_tfidf.entrySet().stream().forEach(en -> {
                String wz = en.getKey();
                en.getValue().entrySet().stream().forEach( e ->{
                    Integer ix = xlCount.getXlCount();
                    // 判断当前词语对应词库位置是否存在
                    if(e.getKey().equals(allwords.get(ix))){
                        // 获取当前文章向量数组
                        double[] d = xlmap.get(wz);
                        d[ix] = e.getValue().doubleValue();
                    }
                });

            });
        }

        // (文章向量集合)计算文章相似度
        // 相似list(相似的归为一类)
        List<Set<String>> xsList = new ArrayList();
        // 标记map（标记后无需再计算）
        HashMap<String, Object> bjmap = new HashMap<>();
        xlmap.entrySet().stream().forEach(xl -> {
            // 从第一个开始
            String key = xl.getKey();
            if(bjmap.get(key) == null){
                // 计算相似
                xlmap.entrySet().stream().forEach(xlcale -> {
                    // 计算相似的文本（不包括自己）
                    if(!xlcale.getKey().equals(key)) {
                        // 没有被标记
                        if(bjmap.get(xlcale.getKey()) == null){
                            // 相似
                            Double cosineXS = cosineXS(xlmap.get(key), xlcale.getValue());
                            if(SIM_THRESHOLD.compareTo(cosineXS) < 0){
                                bjmap.put(xlcale.getKey(), null);
                                XlCount czflag = new XlCount(false);
                                // 加入相似集合中
                                xsList.stream().forEach(xs -> {
                                    if(xs.contains(key) || xs.contains(xlcale.getKey())){
                                        xs.add(key);
                                        xs.add(xlcale.getKey());
                                        czflag.setFlag(true);
                                    }
                                });
                                if(!czflag.isFlag()){
                                    // 没有set
                                    HashSet<String> xsset = new HashSet<>();
                                    xsset.add(key);
                                    xsset.add(xlcale.getKey());
                                    // 添加到总集合中
                                    xsList.add(xsset);
                                }
                            }
                        }
                    }
                });
                // 将当前文本放入标记map中
                bjmap.put(key, null);
                // 当前key加入相似集合
                XlCount czflag = new XlCount(false);
                xsList.stream().forEach(xs -> {
                    if(xs.contains(key)){
                        xs.add(key);
                        czflag.setFlag(true);
                    }
                });
                if(!czflag.isFlag()){
                    // 没有set
                    HashSet<String> xsset = new HashSet<>();
                    xsset.add(key);
                    // 添加到总集合中
                    xsList.add(xsset);
                }
            }
        });

        return xsList;
    }

    /**
     * cos夹角公式
     * @param f1
     * @param f2
     * @return
     */
    public static Double cosineXS(double[] f1, double[] f2){
        int length = f1.length;
        // 向量1的模
        Double result1 = 0.00;
        // 向量2的模
        Double result2 = 0.00;
        Double sum = 0d;
        if(length == 0){
            return 0d;
        }
        for (int i = 0; i < length; i++){
            result1 += f1[i] * f1[i];
            result2 += f2[i] * f2[i];
            // 分子
            sum += f1[i] * f2[i];
        }
        // 分母
        Double result = Math.sqrt(result1 * result2);
        if(result.compareTo(new Double("0")) == 0){
            // 分母为0
            return 0d;
        }
        return sum/result;
    }
}
